﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Xna_Shootr
{
    abstract class GameObject
    {
        protected Texture2D sprite;
        protected Vector2 position = Vector2.Zero;
        protected Vector2 center = Vector2.Zero;
        protected Vector2 velocity = Vector2.Zero;
        protected Vector2 acceleration = Vector2.Zero;
        protected float rotation = 0.0f;

        public GameObject()
        {
        }

        public GameObject(Texture2D sprite)
        {
            this.sprite = sprite;            
            center = new Vector2(sprite.Width / 2, sprite.Height / 2);            
        }

        //public abstract void Draw(SpriteBatch spriteBatch);

        public Vector2 Position
        {
            get { return position; }
            set { position = value; }
        }

        public Vector2 Velocity
        {
            get { return velocity; }
            set { velocity = value; }
        }

        public Vector2 Center
        {
            get { return center; }
            set { center = value; }
        }

        public Vector2 Acceleration
        {
            get { return acceleration; }
            set { acceleration = value; }
        }

        public float Rotation
        {
            get { return rotation; }
            set { rotation = value; }
        }

        public Vector2 GetUnitVelocity()
        {
            return new Vector2(velocity.X / velocity.Length(), velocity.Y / velocity.Length());
        }

        public bool DetectCollision(GameObject obj)
        {
            Rectangle bbox, objBbox;

            bbox = CalulateBBox();
            objBbox = obj.CalulateBBox();
            
            if (bbox.Intersects(objBbox))
                return PerPixelIntersection(obj);
            return false;
            
        }

        // calculate the bounding box of GameObject
        // w, h - GameObject's sprite; w', h' - BBox
        // w' = w * |cos(theta)| + h * |sin(theta)|
        // h' = w * |sin(theta)| + h * |cos(theta)|
        public Rectangle CalulateBBox()
        {
            int w = (int) (sprite.Width*Math.Abs(Math.Cos(rotation)) + sprite.Height*Math.Abs(Math.Sin(rotation)));
            int h = (int) (sprite.Width*Math.Abs(Math.Sin(rotation)) + sprite.Height*Math.Abs(Math.Cos(rotation)));
            int x = (int) (position.X - w/2.0f);
            int y = (int) (position.Y - h/2.0f);

            return new Rectangle(x, y, w, h);
        }

        // perform per-pixel intersection test
        private bool PerPixelIntersection(GameObject obj)
        {
            // copy texture data to arrays for iteration
            Color[] pixels = new Color[sprite.Width * sprite.Height];
            sprite.GetData(pixels);
            Color[] objPixels = new Color[obj.sprite.Width * obj.sprite.Height];
            obj.sprite.GetData(objPixels);

            // difference of rotation between two objects
            float angle = obj.rotation - rotation;
            // base vectors in obj coordinate system project in current coordinate system
            Vector2 u = new Vector2((float) Math.Cos(angle), (float) Math.Sin(angle));
            Vector2 v = new Vector2((float) (-Math.Sin(angle)), (float) Math.Cos(angle));
            Matrix m = Matrix.CreateRotationZ(-rotation);
            // the origin(top-left point) of obj and it's projected position in current object
            Vector2 origin = obj.position +
                             Vector2.Transform(new Vector2(-obj.sprite.Width/2.0f, -obj.sprite.Height/2.0f),
                                               Matrix.CreateRotationZ(obj.rotation));
            Vector2 prjOrigin = Vector2.Transform(origin-position, m);
            Vector2 point;

            for (int w = 0; w < obj.sprite.Width; w++)
            {
                for (int h = 0; h < obj.sprite.Height; h++)
                {
                    if (objPixels[w + h * obj.sprite.Width].A == 0)
                        continue;
                    point = prjOrigin + w*u + v*h;
                    int px = (int) (point.X + sprite.Width / 2.0f);
                    if (px < 0 || px >= sprite.Width)
                        continue;
                    int py = (int) (point.Y + sprite.Height / 2.0f);
                    if (py < 0 || py >= sprite.Height)
                        continue;
                    if (pixels[px + py * sprite.Width].A != 0)
                        return true;
                }
            }
            
            return false;
        }
    }
}
